home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Celestin Apprentice 5
/
Apprentice-Release5.iso
/
Source Code
/
Libraries
/
Dots & Pixels
/
sources
/
flowdots.cp
< prev
next >
Wrap
Text File
|
1995-09-29
|
9KB
|
309 lines
#include <Retrace.h>
#include <Devices.h>
#include <SegLoad.h>
#include <Timer.h>
#include <math.h>
#include <iostream.h>
#include "C_randomizer.h"
#include "flowsettings.h"
#include "vretrace.h"
#include "macutilities.h"
#include "phaser.h"
#include "screenarea.h"
#include "screendots.h"
#include "dotcollection.h"
#include "flowdots.h"
flowdots::flowdots( int numbits, int xpos, int ypos,
int aantaldots, int lifetime, double how_often)
: flowparams()
, dotcollection( aantaldots)
, phaser( lifetime)
, flowsettings()
, screendots( numbits, xpos, ypos, aantaldots)
{
time_of_last_apply_flow_call = 0;
framerate = framerate_of_main_monitor();
if( how_often > 0.0)
{
updaterate = how_often;
auto_timing = false;
} else {
auto_timing = true;
}
compute_matrices();
}
flowdots::flowdots( int numbits, screen_position where,
int aantaldots, int lifetime, double how_often)
: flowparams()
, dotcollection( aantaldots)
, phaser( lifetime)
, flowsettings()
, screendots( numbits, where, aantaldots)
{
time_of_last_apply_flow_call = 0;
framerate = framerate_of_main_monitor();
if( how_often > 0.0)
{
updaterate = how_often;
auto_timing = false;
} else {
auto_timing = true;
}
compute_matrices();
}
void flowdots::compute_addresses()
{
apply_flow(); // computes next position of all dots
//
// could use dotcollection::numdots, as well
//
#define help_the_optimizer
#ifdef help_the_optimizer
const int loopend = screendots::numdots;
const int the_shift = coord_shift; // aids the optimizer
unsigned char **dot_address = dot_addresses;
short *xcoord = xcoords;
short *ycoord = ycoords;
for( int i = 0; i < loopend; i++)
{
*dot_address++ =
&screen[ *ycoord++ >> the_shift][ *xcoord++ >> the_shift];
}
#else
for( int i = 0; i < screendots::numdots; i++)
{
dot_addresses[ i] =
&screen[ ycoords[ i] >> coord_shift][ xcoords[ i] >> coord_shift];
}
#endif
#undef help_the_optimizer
}
void flowdots::apply_flow()
{
(void)major_step();
if( auto_timing)
{
//
// maintain time yourself:
//
const unsigned long now_micros = readMicroseconds();
if( time_of_last_apply_flow_call == 0)
{
//
// first time around: 'smart' guess
//
updaterate = framerate / 2;
} else {
#ifdef THINK_CPLUS
//
// prevents a call of '_UTOX96' (SC++ 7.0 bug)
// (941121: bug no longer really present in 7.0.4. It used to be
// that a call to '_UTOX96' was generated, where this function
// was not present in 'ANSI++ 881' (or any other '881 library).
// With SC++ 7.0.4 the call to '_UTOX91' is still generated, but
// no link errors occur, but for now, keep using a long instead
// of an unsigned long. (this replaces a subroutine call with a
// FPU divide. The latter is much faster, and delivers the same
// result, as long as this code doesn't run on a Pentium ;-)
//
const long delay = now_micros - time_of_last_apply_flow_call;
#else
const unsigned long delay = now_micros - time_of_last_apply_flow_call;
#endif
updaterate = 1000000.0 / (double)delay;
}
time_of_last_apply_flow_call = now_micros;
compute_matrices();
}
short *the_xcoord = xcoords;
short *the_ycoord = ycoords;
//
// Note: there must be _exactly_ one '*the_xcoord++ = …' and _exactly_ one
// '*the_ycoord++ = …' in every possible path through the loop.
//
short_long_hack the_hack;
const int loopend = dotcollection::numdots; // could also use screendots::numdots
for( int i = 0; i < loopend; i++)
{
if( minor_step() != 0)
{
//
// compute new position of dot
//
const long prev_x = (long) *the_xcoord;
const long prev_y = (long) *the_ycoord;
//
// Note: the expression below was optimized, but now gives slightly different
// results. I haven't thought about its ramifications, yet.
// 941206: The difference is thought to be too minor to be visible.
//
// const long new_x = ((prev_x * x11) >> 15)
// + ((prev_y * x12) >> 15) + trans_x + prev_x;
//
const long new_x =
(((prev_x * x11) + (prev_y * x12)) >> 15) + trans_x + prev_x;
if( new_x != (short)new_x)
{
//
// use one call of 'step' and split the result in two:
//
the_hack.ulong = randomizer_step();
*the_xcoord++ = the_hack.shorties.left;
*the_ycoord++ = the_hack.shorties.right;
} else {
//
// Note: the expression below was optimized, but now gives slightly different
// results. I haven't thought about its ramifications, yet.
// 941206: The difference is thought to be too minor to be visible.
//
// const long new_y = ((prev_x * x21) >> 15)
// + ((prev_y * x22) >> 15) + trans_y + prev_y;
//
const long new_y =
(((prev_x * x21) + (prev_y * x22)) >> 15) + trans_y + prev_y;
if( new_y != (short)new_y)
{
//
// use one call of 'step' and split the result in two:
//
the_hack.ulong = randomizer_step();
*the_xcoord++ = the_hack.shorties.left;
*the_ycoord++ = the_hack.shorties.right;
} else {
*the_xcoord++ = (short)new_x;
*the_ycoord++ = (short)new_y;
}
}
} else {
//
// dot surpassed its lifetime
//
the_hack.ulong = randomizer_step();
*the_xcoord++ = the_hack.shorties.left;
*the_ycoord++ = the_hack.shorties.right;
}
}
}
void flowdots::compute_matrices()
{
//
// The translation will not be stored in the matrix (in fact it can't be).
// There is no need to preprocess it since its parameters are available
// in the 'translation' parameter (translation.x, translation.y);
//
// view a shear in direction d as a rotation over -d, a 'natural shear',
// and a rotation back over d.
// Let d be the shear angle and m its magnitude. The shear matrix can than
// be calculated as:
//
// ( cos( d) -sin( d) ) ( m 0 ) ( cos(-d) -sin(-d) )
// ( sin( d) cos( d) ) ( 0 1/m) ( sin(-d) cos(-d) )
//
// Moreover we have sin(-x) = -sin( x), cos(-x) = cos( x)
// and we can define c := cos( d), s := sin( d) for brevity.
// We then find
//
// ( c -s ) ( m 0 ) ( c s )
// ( s c ) ( 0 1/m) (-s c )
//
// which (with c2 = c squared, etc) is equal to
//
// ( c2 m + s2/m cs (m - 1/m) )
// ( cs (m - 1/m) s2 m + c2/m )
//
// These values will be put in the transformation matrix as follows:
//
// ( xform[ 0] xform[ 1] )
// ( xform[ 2] xform[ 3] )
//
// (well, almost in the transformation matrix; it appears to be simpler
// to use copies called xform_0, xform_1, xform_2, xform_3)
// Also, we do not store the transformation matrix, but we store the
// difference between the transformation matrix and the identity matrix.
//
// But first, we derive 'per frame' values for the flow parameters.
//
const double scaled_expansion = scale( expansion, updaterate);
const double scaled_shear_magnitude = scale( shear_magnitude, updaterate);
const double scaled_rotation = rotation / updaterate;
const double cos_dir = cos( shear_direction degrees);
const double sin_dir = sin( shear_direction degrees);
const double cos_squared = cos_dir * cos_dir;
const double sin_squared = sin_dir * sin_dir;
const double cos_sin = cos_dir * sin_dir;
const double m = scaled_shear_magnitude;
const double one_over_m = 1 / m;
const double xform_0 = cos_squared * m + sin_squared / m;
const double xform_1 = cos_sin * (m - one_over_m);
const double xform_2 = xform_1;
const double xform_3 = sin_squared * m + cos_squared / m;
//
// now apply the rotation:
//
// the transformation matrix for this operation is
//
// ( cos( r) -sin( r) )
// ( sin( r) cos( r) )
//
const double cos_rot = cos( scaled_rotation degrees);
const double sin_rot = sin( scaled_rotation degrees);
double xform[ 4];
xform[ 0] = cos_rot * xform_0 - sin_rot * xform_2;
xform[ 1] = cos_rot * xform_1 - sin_rot * xform_3;
xform[ 2] = sin_rot * xform_0 + cos_rot * xform_2;
xform[ 3] = sin_rot * xform_1 + cos_rot * xform_3;
//
// apply the expansion (unrolling the loop):
//
xform[ 0] *= scaled_expansion;
xform[ 1] *= scaled_expansion;
xform[ 2] *= scaled_expansion;
xform[ 3] *= scaled_expansion;
x11 = (short)(long)((xform[ 0] - 1.0) * 0x8000);
x12 = (short)(long)(xform[ 1] * 0x8000);
x21 = (short)(long)(xform[ 2] * 0x8000);
x22 = (short)(long)((xform[ 3] - 1.0) * 0x8000);
trans_x = (long)(translation_x * 0x10000 / updaterate);
trans_y = (long)(translation_y * 0x10000 / updaterate);
}
void flowdots::setsettings( const flowsettings &thesettings)
{
*((flowsettings *)this) = thesettings;
changed();
}
void flowdots::setsettings( const flowparams &theparams)
{
//
// This safety check could be added, but this function is written to obtain speed,
// so we leave it out for the moment.
//
// if( how_often == 0.0)
// {
// DebugStr( "\pflowdots::setsettings( flowparams) called while how_often == 0.0");
// }
//
*((flowparams *)this) = theparams;
}